13.2 nycflights13

13.2.1 Exercises

library(tidyverse)
Loading tidyverse: ggplot2
Loading tidyverse: tibble
Loading tidyverse: tidyr
Loading tidyverse: readr
Loading tidyverse: purrr
Loading tidyverse: dplyr
Conflicts with tidy packages -------------------
filter(): dplyr, stats
lag():    dplyr, stats
library(nycflights13)
  1. Imagine you wanted to draw (approximately) the route each plane flies from its origin to its destination. What variables would you need? What tables would you need to combine?

You would need origin and dest for each flight, along with the lat and long for each origin and destination airport. This means that you would need to combine the flights and airports tables.

  1. I forgot to draw the relationship between weather and airports. What is the relationship and how should it appear in the diagram?

origin in weather connects to faa in airports.

  1. weather only contains information for the origin (NYC) airports. If it contained weather records for all airports in the USA, what additional relation would it define with flights?

It would connect with dest in flightsso that weather records for the destination airport could be identified as well.

  1. We know that some days of the year are “special”, and fewer people than usual fly on them. How might you represent that data as a data frame? What would be the primary keys of that table? How would it connect to the existing tables?

You could have a specialdays table with a dayname as its primary key and then connect to flights via year, month and day variables.

13.3 Keys

13.3.1 Exercises

  1. Add a surrogate key to flights.
flights <- flights %>%
  mutate(flight_id = row_number())
flights %>%
  count(flight_id) %>%
  filter(n > 1)
  1. Identify the keys in the following datasets

  2. Lahman::Batting,
  3. babynames::babynames
  4. nasaweather::atmos
  5. fueleconomy::vehicles
  6. ggplot2::diamonds

(You might need to install some packages and read some documentation.)

library(Lahman)
Batting
Batting %>%
  count(playerID, yearID, stint) %>%
  filter(n > 1)
library(babynames)
babynames
babynames %>%
  count(year, sex, name) %>%
  filter(nn > 1)
library(nasaweather)
atmos
atmos %>%
  count(lat, long, year, month) %>%
  filter(n > 1)
library(fueleconomy)
vehicles
vehicles %>%
  count(id) %>%
  filter(n > 1)
diamonds

This doesn’t appear to have a primary key because each combination of diamond attributes wouldn’t be guaranteed to be unique (even if they are unique within the current data set). It would be best to create a surrogate key.

diamonds <- diamonds %>%
  mutate(id = row_number())
  1. Draw a diagram illustrating the connections between the Batting, Master, and Salaries tables in the Lahman package. Draw another diagram that shows the relationship between Master, Managers, AwardsManagers.

Batting$playerID connects to Master$playerID. Salaries$playerID connects to Master$playerID. Batting and Salaries could be connected via playerID, yearID, and teamID.

Master$playerID connects to Managers$playerID. Master$playerID connects to AwardsManagers$playerID. Managers and AwardsManagers via playerID and yearID.

How would you characterise the relationship between the Batting, Pitching, and Fielding tables?

Batting
Pitching
Fielding

They should have a one-to-one relationship with matching primary keys (playerID, yearID, stint) across the three tables - with most position players only having records for Batting and Fielding, while pitchers are likely to have records only in Pitching and Fielding.

13.4 Mutating joins

13.4.6 Exercises

  1. Compute the average delay by destination, then join on the airports data frame so you can show the spatial distribution of delays. Here’s an easy way to draw a map of the United States:
airports %>%
  semi_join(flights, c("faa" = "dest")) %>%
  ggplot(aes(lon, lat)) +
    borders("state") +
    geom_point() +
    coord_quickmap()

Attaching package: ‘maps’

The following object is masked from ‘package:purrr’:

    map

(Don’t worry if you don’t understand what semi_join() does — you’ll learn about it next.)

You might want to use the size or colour of the points to display the average delay for each airport.

flights %>%
  group_by(dest) %>%
  summarise(avg_delay = mean(arr_delay, na.rm = TRUE)) %>%
  left_join(airports, c("dest" = "faa")) %>%
  ggplot(aes(lon, lat)) +
    borders("state") +
    geom_point(aes(colour = avg_delay)) +
    coord_quickmap()

  1. Add the location of the origin and destination (i.e. the lat and lon) to flights.
flights %>%
  left_join(airports, by = c("dest" = "faa")) %>%
  left_join(airports, by = c("origin" = "faa"))
  1. Is there a relationship between the age of a plane and its delays?
flights %>%
  group_by(tailnum) %>%
  summarise(avg_delay = mean(arr_delay, na.rm = TRUE)) %>%
  right_join(planes) %>%
  ggplot(aes(year, avg_delay)) +
    geom_point()
Joining, by = "tailnum"

There doesn’t appear to be a clear relationship.

  1. What weather conditions make it more likely to see a delay?
flights %>%
  group_by(year, month, day, hour) %>%
  summarise(avg_delay = mean(dep_delay, na.rm = TRUE)) %>%
  left_join(weather) %>%
  ggplot(aes(temp, avg_delay)) +
    geom_point(alpha = 0.1)
Joining, by = c("year", "month", "day", "hour")

flights %>%
  group_by(year, month, day, hour) %>%
  summarise(avg_delay = mean(dep_delay, na.rm = TRUE)) %>%
  left_join(weather) %>%
  ggplot(aes(wind_speed, avg_delay)) +
    geom_point(alpha = 0.1)
Joining, by = c("year", "month", "day", "hour")

flights %>%
  group_by(year, month, day, hour) %>%
  summarise(avg_delay = mean(dep_delay, na.rm = TRUE)) %>%
  left_join(weather) %>%
  ggplot(aes(precip, avg_delay)) +
    geom_point(alpha = 0.1)
Joining, by = c("year", "month", "day", "hour")

flights %>%
  group_by(year, month, day, hour) %>%
  summarise(avg_delay = mean(dep_delay, na.rm = TRUE)) %>%
  left_join(weather) %>%
  ggplot(aes(pressure, avg_delay)) +
    geom_point(alpha = 0.1)
Joining, by = c("year", "month", "day", "hour")

flights %>%
  group_by(year, month, day, hour) %>%
  summarise(avg_delay = mean(dep_delay, na.rm = TRUE)) %>%
  left_join(weather) %>%
  ggplot(aes(visib, avg_delay)) +
    geom_point(alpha = 0.1)
Joining, by = c("year", "month", "day", "hour")

  1. What happened on June 13 2013? Display the spatial pattern of delays, and then use Google to cross-reference with the weather.
flights %>%
  filter(year == 2013, month == 6, day == 13) %>%
  group_by(dest) %>%
  summarise(avg_delay = mean(arr_delay, na.rm = TRUE)) %>%
  left_join(airports, c("dest" = "faa")) %>%
  ggplot(aes(lon, lat)) +
    borders("state") +
    geom_point(aes(colour = avg_delay)) +
    coord_quickmap()

There were severe storms in the mid-Atlantic region.

13.5 Filtering joins

13.5.1 Exercises

  1. What does it mean for a flight to have a missing tailnum? What do the tail numbers that don’t have a matching record in planes have in common? (Hint: one variable explains ~90% of the problems.)
flights %>%
  anti_join(planes, by = "tailnum") %>%
  count(carrier)
?planes

A missing tailnum means that the metadata for that plane hasn’t been recorded. The vast majority of these cases come from two carriers (AA and MQ). Looking at the help file for planes explains that these two carriers report fleet numbers rather than tail numbers.

  1. Filter flights to only show flights with planes that have flown at least 100 flights.
flights_100 <- flights %>%
  count(tailnum) %>%
  filter(n >= 100)
semi_join(flights, flights_100)
Joining, by = "tailnum"
  1. Combine fueleconomy::vehicles and fueleconomy::common to find only the records for the most common models.
semi_join(vehicles, common)
Joining, by = c("make", "model")
  1. Find the 48 hours (over the course of the whole year) that have the worst delays. Cross-reference it with the weather data. Can you see any patterns?
worst_delays <- flights %>%
  group_by(year, month, day, hour) %>%
  summarise(avg_delay = mean(dep_delay, na.rm = TRUE)) %>%
  arrange(desc(avg_delay)) %>%
  head(48)
semi_join(weather, worst_delays) %>%
  ggplot(aes(temp)) +
    geom_histogram()
Joining, by = c("year", "month", "day", "hour")

It’s possible to plot histograms for the different weather variables in a search for patterns. The temperature example above suggests that sometimes the delays happened on quite cold days but more of them happened in mild or warm weather.

  1. What does anti_join(flights, airports, by = c("dest" = "faa")) tell you? What does anti_join(airports, flights, by = c("faa" = "dest")) tell you?
anti_join(flights, airports, by = c("dest" = "faa"))

This shows flights that went to a destination not listed in the airports database - from a quick inspection and search, many of these seem to be to airports in Puerto Rico or elsewhere in the Caribbean, ie outside the mainland United States.

anti_join(airports, flights, by = c("faa" = "dest"))

This shows airports that are listed in the airports database but which weren’t the destination for any flights in the flights database.

  1. You might expect that there’s an implicit relationship between plane and airline, because each plane is flown by a single airline. Confirm or reject this hypothesis using the tools you’ve learned above.
# Identify any planes (by `tailnum` that didn't have only a single carrier recorded for their flight records)
multi_carrier <- flights %>%
  count(tailnum, carrier) %>%
  count(tailnum) %>%
  filter(nn != 1)
semi_join(flights, multi_carrier) %>%
  count(tailnum, carrier)
Joining, by = "tailnum"
LS0tCnRpdGxlOiAiQ2hhcHRlciAxMzogUmVsYXRpb25hbCBkYXRhIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgojIDEzLjIgYG55Y2ZsaWdodHMxM2AKCiMjIDEzLjIuMSBFeGVyY2lzZXMKCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShueWNmbGlnaHRzMTMpCmBgYAoKMS4gSW1hZ2luZSB5b3Ugd2FudGVkIHRvIGRyYXcgKGFwcHJveGltYXRlbHkpIHRoZSByb3V0ZSBlYWNoIHBsYW5lIGZsaWVzIGZyb20gaXRzIG9yaWdpbiB0byBpdHMgZGVzdGluYXRpb24uIFdoYXQgdmFyaWFibGVzIHdvdWxkIHlvdSBuZWVkPyBXaGF0IHRhYmxlcyB3b3VsZCB5b3UgbmVlZCB0byBjb21iaW5lPwoKKipZb3Ugd291bGQgbmVlZCBgb3JpZ2luYCBhbmQgYGRlc3RgIGZvciBlYWNoIGZsaWdodCwgYWxvbmcgd2l0aCB0aGUgYGxhdGAgYW5kIGBsb25nYCBmb3IgZWFjaCBvcmlnaW4gYW5kIGRlc3RpbmF0aW9uIGFpcnBvcnQuIFRoaXMgbWVhbnMgdGhhdCB5b3Ugd291bGQgbmVlZCB0byBjb21iaW5lIHRoZSBgZmxpZ2h0c2AgYW5kIGBhaXJwb3J0c2AgdGFibGVzLioqCgoyLiBJIGZvcmdvdCB0byBkcmF3IHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBgd2VhdGhlcmAgYW5kIGBhaXJwb3J0c2AuIFdoYXQgaXMgdGhlIHJlbGF0aW9uc2hpcCBhbmQgaG93IHNob3VsZCBpdCBhcHBlYXIgaW4gdGhlIGRpYWdyYW0/CgoqKmBvcmlnaW5gIGluIGB3ZWF0aGVyYCBjb25uZWN0cyB0byBgZmFhYCBpbiBgYWlycG9ydHNgLioqCgozLiBgd2VhdGhlcmAgb25seSBjb250YWlucyBpbmZvcm1hdGlvbiBmb3IgdGhlIG9yaWdpbiAoTllDKSBhaXJwb3J0cy4gSWYgaXQgY29udGFpbmVkIHdlYXRoZXIgcmVjb3JkcyBmb3IgYWxsIGFpcnBvcnRzIGluIHRoZSBVU0EsIHdoYXQgYWRkaXRpb25hbCByZWxhdGlvbiB3b3VsZCBpdCBkZWZpbmUgd2l0aCBmbGlnaHRzPwoKKipJdCB3b3VsZCBjb25uZWN0IHdpdGggYGRlc3RgIGluIGBmbGlnaHRzYHNvIHRoYXQgd2VhdGhlciByZWNvcmRzIGZvciB0aGUgZGVzdGluYXRpb24gYWlycG9ydCBjb3VsZCBiZSBpZGVudGlmaWVkIGFzIHdlbGwuKioKCjQuIFdlIGtub3cgdGhhdCBzb21lIGRheXMgb2YgdGhlIHllYXIgYXJlIOKAnHNwZWNpYWzigJ0sIGFuZCBmZXdlciBwZW9wbGUgdGhhbiB1c3VhbCBmbHkgb24gdGhlbS4gSG93IG1pZ2h0IHlvdSByZXByZXNlbnQgdGhhdCBkYXRhIGFzIGEgZGF0YSBmcmFtZT8gV2hhdCB3b3VsZCBiZSB0aGUgcHJpbWFyeSBrZXlzIG9mIHRoYXQgdGFibGU/IEhvdyB3b3VsZCBpdCBjb25uZWN0IHRvIHRoZSBleGlzdGluZyB0YWJsZXM/CgoqKllvdSBjb3VsZCBoYXZlIGEgYHNwZWNpYWxkYXlzYCB0YWJsZSB3aXRoIGEgYGRheW5hbWVgIGFzIGl0cyBwcmltYXJ5IGtleSBhbmQgdGhlbiBjb25uZWN0IHRvIGBmbGlnaHRzYCB2aWEgYHllYXJgLCBgbW9udGhgIGFuZCBgZGF5YCB2YXJpYWJsZXMuKioKCiMgMTMuMyBLZXlzCgojIyAxMy4zLjEgRXhlcmNpc2VzCgoxLiBBZGQgYSBzdXJyb2dhdGUga2V5IHRvIGBmbGlnaHRzYC4KCmBgYHtyfQpmbGlnaHRzIDwtIGZsaWdodHMgJT4lCiAgbXV0YXRlKGZsaWdodF9pZCA9IHJvd19udW1iZXIoKSkKZmxpZ2h0cyAlPiUKICBjb3VudChmbGlnaHRfaWQpICU+JQogIGZpbHRlcihuID4gMSkKYGBgCgoyLiBJZGVudGlmeSB0aGUga2V5cyBpbiB0aGUgZm9sbG93aW5nIGRhdGFzZXRzCgogIDEuIGBMYWhtYW46OkJhdHRpbmdgLAogIDIuIGBiYWJ5bmFtZXM6OmJhYnluYW1lc2AKICAzLiBgbmFzYXdlYXRoZXI6OmF0bW9zYAogIDQuIGBmdWVsZWNvbm9teTo6dmVoaWNsZXNgCiAgNS4gYGdncGxvdDI6OmRpYW1vbmRzYAoKKFlvdSBtaWdodCBuZWVkIHRvIGluc3RhbGwgc29tZSBwYWNrYWdlcyBhbmQgcmVhZCBzb21lIGRvY3VtZW50YXRpb24uKQoKYGBge3J9CmxpYnJhcnkoTGFobWFuKQpCYXR0aW5nCmBgYAoKYGBge3J9CkJhdHRpbmcgJT4lCiAgY291bnQocGxheWVySUQsIHllYXJJRCwgc3RpbnQpICU+JQogIGZpbHRlcihuID4gMSkKYGBgCgpgYGB7cn0KbGlicmFyeShiYWJ5bmFtZXMpCmJhYnluYW1lcwpgYGAKCmBgYHtyfQpiYWJ5bmFtZXMgJT4lCiAgY291bnQoeWVhciwgc2V4LCBuYW1lKSAlPiUKICBmaWx0ZXIobm4gPiAxKQpgYGAKCmBgYHtyfQpsaWJyYXJ5KG5hc2F3ZWF0aGVyKQphdG1vcwpgYGAKCmBgYHtyfQphdG1vcyAlPiUKICBjb3VudChsYXQsIGxvbmcsIHllYXIsIG1vbnRoKSAlPiUKICBmaWx0ZXIobiA+IDEpCmBgYAoKYGBge3J9CmxpYnJhcnkoZnVlbGVjb25vbXkpCnZlaGljbGVzCmBgYAoKYGBge3J9CnZlaGljbGVzICU+JQogIGNvdW50KGlkKSAlPiUKICBmaWx0ZXIobiA+IDEpCmBgYAoKYGBge3J9CmRpYW1vbmRzCmBgYAoKKipUaGlzIGRvZXNuJ3QgYXBwZWFyIHRvIGhhdmUgYSBwcmltYXJ5IGtleSBiZWNhdXNlIGVhY2ggY29tYmluYXRpb24gb2YgZGlhbW9uZCBhdHRyaWJ1dGVzIHdvdWxkbid0IGJlIGd1YXJhbnRlZWQgdG8gYmUgdW5pcXVlIChldmVuIGlmIHRoZXkgYXJlIHVuaXF1ZSB3aXRoaW4gdGhlIGN1cnJlbnQgZGF0YSBzZXQpLiBJdCB3b3VsZCBiZSBiZXN0IHRvIGNyZWF0ZSBhIHN1cnJvZ2F0ZSBrZXkuKioKCmBgYHtyfQpkaWFtb25kcyA8LSBkaWFtb25kcyAlPiUKICBtdXRhdGUoaWQgPSByb3dfbnVtYmVyKCkpCmBgYAoKMy4gRHJhdyBhIGRpYWdyYW0gaWxsdXN0cmF0aW5nIHRoZSBjb25uZWN0aW9ucyBiZXR3ZWVuIHRoZSBgQmF0dGluZ2AsIGBNYXN0ZXJgLCBhbmQgYFNhbGFyaWVzYCB0YWJsZXMgaW4gdGhlIExhaG1hbiBwYWNrYWdlLiBEcmF3IGFub3RoZXIgZGlhZ3JhbSB0aGF0IHNob3dzIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBgTWFzdGVyYCwgYE1hbmFnZXJzYCwgYEF3YXJkc01hbmFnZXJzYC4KCioqYEJhdHRpbmckcGxheWVySURgIGNvbm5lY3RzIHRvIGBNYXN0ZXIkcGxheWVySURgLiBgU2FsYXJpZXMkcGxheWVySURgIGNvbm5lY3RzIHRvIGBNYXN0ZXIkcGxheWVySURgLiBgQmF0dGluZ2AgYW5kIGBTYWxhcmllc2AgY291bGQgYmUgY29ubmVjdGVkIHZpYSBgcGxheWVySURgLCBgeWVhcklEYCwgYW5kIGB0ZWFtSURgLioqCgoqKmBNYXN0ZXIkcGxheWVySURgIGNvbm5lY3RzIHRvIGBNYW5hZ2VycyRwbGF5ZXJJRGAuIGBNYXN0ZXIkcGxheWVySURgIGNvbm5lY3RzIHRvIGBBd2FyZHNNYW5hZ2VycyRwbGF5ZXJJRGAuIGBNYW5hZ2Vyc2AgYW5kIGBBd2FyZHNNYW5hZ2Vyc2AgdmlhIGBwbGF5ZXJJRGAgYW5kIGB5ZWFySURgLioqCgpIb3cgd291bGQgeW91IGNoYXJhY3RlcmlzZSB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIGBCYXR0aW5nYCwgYFBpdGNoaW5nYCwgYW5kIGBGaWVsZGluZ2AgdGFibGVzPwoKYGBge3J9CkJhdHRpbmcKUGl0Y2hpbmcKRmllbGRpbmcKYGBgCgoqKlRoZXkgc2hvdWxkIGhhdmUgYSBvbmUtdG8tb25lIHJlbGF0aW9uc2hpcCB3aXRoIG1hdGNoaW5nIHByaW1hcnkga2V5cyAoYHBsYXllcklEYCwgYHllYXJJRGAsIGBzdGludGApIGFjcm9zcyB0aGUgdGhyZWUgdGFibGVzIC0gd2l0aCBtb3N0IHBvc2l0aW9uIHBsYXllcnMgb25seSBoYXZpbmcgcmVjb3JkcyBmb3IgYEJhdHRpbmdgIGFuZCBgRmllbGRpbmdgLCB3aGlsZSBwaXRjaGVycyBhcmUgbGlrZWx5IHRvIGhhdmUgcmVjb3JkcyBvbmx5IGluIGBQaXRjaGluZ2AgYW5kIGBGaWVsZGluZ2AuKioKCiMgMTMuNCBNdXRhdGluZyBqb2lucwoKIyMgMTMuNC42IEV4ZXJjaXNlcwoKMS4gQ29tcHV0ZSB0aGUgYXZlcmFnZSBkZWxheSBieSBkZXN0aW5hdGlvbiwgdGhlbiBqb2luIG9uIHRoZSBgYWlycG9ydHNgIGRhdGEgZnJhbWUgc28geW91IGNhbiBzaG93IHRoZSBzcGF0aWFsIGRpc3RyaWJ1dGlvbiBvZiBkZWxheXMuIEhlcmXigJlzIGFuIGVhc3kgd2F5IHRvIGRyYXcgYSBtYXAgb2YgdGhlIFVuaXRlZCBTdGF0ZXM6CgpgYGB7cn0KYWlycG9ydHMgJT4lCiAgc2VtaV9qb2luKGZsaWdodHMsIGMoImZhYSIgPSAiZGVzdCIpKSAlPiUKICBnZ3Bsb3QoYWVzKGxvbiwgbGF0KSkgKwogICAgYm9yZGVycygic3RhdGUiKSArCiAgICBnZW9tX3BvaW50KCkgKwogICAgY29vcmRfcXVpY2ttYXAoKQpgYGAKKERvbuKAmXQgd29ycnkgaWYgeW91IGRvbuKAmXQgdW5kZXJzdGFuZCB3aGF0IGBzZW1pX2pvaW4oKWAgZG9lcyDigJQgeW914oCZbGwgbGVhcm4gYWJvdXQgaXQgbmV4dC4pCgpZb3UgbWlnaHQgd2FudCB0byB1c2UgdGhlIGBzaXplYCBvciBgY29sb3VyYCBvZiB0aGUgcG9pbnRzIHRvIGRpc3BsYXkgdGhlIGF2ZXJhZ2UgZGVsYXkgZm9yIGVhY2ggYWlycG9ydC4KCmBgYHtyfQpmbGlnaHRzICU+JQogIGdyb3VwX2J5KGRlc3QpICU+JQogIHN1bW1hcmlzZShhdmdfZGVsYXkgPSBtZWFuKGFycl9kZWxheSwgbmEucm0gPSBUUlVFKSkgJT4lCiAgbGVmdF9qb2luKGFpcnBvcnRzLCBjKCJkZXN0IiA9ICJmYWEiKSkgJT4lCiAgZ2dwbG90KGFlcyhsb24sIGxhdCkpICsKICAgIGJvcmRlcnMoInN0YXRlIikgKwogICAgZ2VvbV9wb2ludChhZXMoY29sb3VyID0gYXZnX2RlbGF5KSkgKwogICAgY29vcmRfcXVpY2ttYXAoKQpgYGAKCjIuIEFkZCB0aGUgbG9jYXRpb24gb2YgdGhlIG9yaWdpbiBhbmQgZGVzdGluYXRpb24gKGkuZS4gdGhlIGBsYXRgIGFuZCBgbG9uYCkgdG8gYGZsaWdodHNgLgoKYGBge3J9CmZsaWdodHMgJT4lCiAgbGVmdF9qb2luKGFpcnBvcnRzLCBieSA9IGMoImRlc3QiID0gImZhYSIpKSAlPiUKICBsZWZ0X2pvaW4oYWlycG9ydHMsIGJ5ID0gYygib3JpZ2luIiA9ICJmYWEiKSkKYGBgCgozLiBJcyB0aGVyZSBhIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSBhZ2Ugb2YgYSBwbGFuZSBhbmQgaXRzIGRlbGF5cz8KCmBgYHtyfQpmbGlnaHRzICU+JQogIGdyb3VwX2J5KHRhaWxudW0pICU+JQogIHN1bW1hcmlzZShhdmdfZGVsYXkgPSBtZWFuKGFycl9kZWxheSwgbmEucm0gPSBUUlVFKSkgJT4lCiAgcmlnaHRfam9pbihwbGFuZXMpICU+JQogIGdncGxvdChhZXMoeWVhciwgYXZnX2RlbGF5KSkgKwogICAgZ2VvbV9wb2ludCgpCmBgYAoKKipUaGVyZSBkb2Vzbid0IGFwcGVhciB0byBiZSBhIGNsZWFyIHJlbGF0aW9uc2hpcC4qKgoKNC4gV2hhdCB3ZWF0aGVyIGNvbmRpdGlvbnMgbWFrZSBpdCBtb3JlIGxpa2VseSB0byBzZWUgYSBkZWxheT8KCmBgYHtyfQpmbGlnaHRzICU+JQogIGdyb3VwX2J5KHllYXIsIG1vbnRoLCBkYXksIGhvdXIpICU+JQogIHN1bW1hcmlzZShhdmdfZGVsYXkgPSBtZWFuKGRlcF9kZWxheSwgbmEucm0gPSBUUlVFKSkgJT4lCiAgbGVmdF9qb2luKHdlYXRoZXIpICU+JQogIGdncGxvdChhZXModGVtcCwgYXZnX2RlbGF5KSkgKwogICAgZ2VvbV9wb2ludChhbHBoYSA9IDAuMSkKZmxpZ2h0cyAlPiUKICBncm91cF9ieSh5ZWFyLCBtb250aCwgZGF5LCBob3VyKSAlPiUKICBzdW1tYXJpc2UoYXZnX2RlbGF5ID0gbWVhbihkZXBfZGVsYXksIG5hLnJtID0gVFJVRSkpICU+JQogIGxlZnRfam9pbih3ZWF0aGVyKSAlPiUKICBnZ3Bsb3QoYWVzKHdpbmRfc3BlZWQsIGF2Z19kZWxheSkpICsKICAgIGdlb21fcG9pbnQoYWxwaGEgPSAwLjEpCmZsaWdodHMgJT4lCiAgZ3JvdXBfYnkoeWVhciwgbW9udGgsIGRheSwgaG91cikgJT4lCiAgc3VtbWFyaXNlKGF2Z19kZWxheSA9IG1lYW4oZGVwX2RlbGF5LCBuYS5ybSA9IFRSVUUpKSAlPiUKICBsZWZ0X2pvaW4od2VhdGhlcikgJT4lCiAgZ2dwbG90KGFlcyhwcmVjaXAsIGF2Z19kZWxheSkpICsKICAgIGdlb21fcG9pbnQoYWxwaGEgPSAwLjEpCmZsaWdodHMgJT4lCiAgZ3JvdXBfYnkoeWVhciwgbW9udGgsIGRheSwgaG91cikgJT4lCiAgc3VtbWFyaXNlKGF2Z19kZWxheSA9IG1lYW4oZGVwX2RlbGF5LCBuYS5ybSA9IFRSVUUpKSAlPiUKICBsZWZ0X2pvaW4od2VhdGhlcikgJT4lCiAgZ2dwbG90KGFlcyhwcmVzc3VyZSwgYXZnX2RlbGF5KSkgKwogICAgZ2VvbV9wb2ludChhbHBoYSA9IDAuMSkKZmxpZ2h0cyAlPiUKICBncm91cF9ieSh5ZWFyLCBtb250aCwgZGF5LCBob3VyKSAlPiUKICBzdW1tYXJpc2UoYXZnX2RlbGF5ID0gbWVhbihkZXBfZGVsYXksIG5hLnJtID0gVFJVRSkpICU+JQogIGxlZnRfam9pbih3ZWF0aGVyKSAlPiUKICBnZ3Bsb3QoYWVzKHZpc2liLCBhdmdfZGVsYXkpKSArCiAgICBnZW9tX3BvaW50KGFscGhhID0gMC4xKQpgYGAKCjUuIFdoYXQgaGFwcGVuZWQgb24gSnVuZSAxMyAyMDEzPyBEaXNwbGF5IHRoZSBzcGF0aWFsIHBhdHRlcm4gb2YgZGVsYXlzLCBhbmQgdGhlbiB1c2UgR29vZ2xlIHRvIGNyb3NzLXJlZmVyZW5jZSB3aXRoIHRoZSB3ZWF0aGVyLgoKYGBge3J9CmZsaWdodHMgJT4lCiAgZmlsdGVyKHllYXIgPT0gMjAxMywgbW9udGggPT0gNiwgZGF5ID09IDEzKSAlPiUKICBncm91cF9ieShkZXN0KSAlPiUKICBzdW1tYXJpc2UoYXZnX2RlbGF5ID0gbWVhbihhcnJfZGVsYXksIG5hLnJtID0gVFJVRSkpICU+JQogIGxlZnRfam9pbihhaXJwb3J0cywgYygiZGVzdCIgPSAiZmFhIikpICU+JQogIGdncGxvdChhZXMobG9uLCBsYXQpKSArCiAgICBib3JkZXJzKCJzdGF0ZSIpICsKICAgIGdlb21fcG9pbnQoYWVzKGNvbG91ciA9IGF2Z19kZWxheSkpICsKICAgIGNvb3JkX3F1aWNrbWFwKCkKYGBgCgoqKlRoZXJlIHdlcmUgc2V2ZXJlIHN0b3JtcyBpbiB0aGUgbWlkLUF0bGFudGljIHJlZ2lvbi4qKgoKIyAxMy41IEZpbHRlcmluZyBqb2lucwoKIyMgMTMuNS4xIEV4ZXJjaXNlcwoKMS4gV2hhdCBkb2VzIGl0IG1lYW4gZm9yIGEgZmxpZ2h0IHRvIGhhdmUgYSBtaXNzaW5nIGB0YWlsbnVtYD8gV2hhdCBkbyB0aGUgdGFpbCBudW1iZXJzIHRoYXQgZG9u4oCZdCBoYXZlIGEgbWF0Y2hpbmcgcmVjb3JkIGluIHBsYW5lcyBoYXZlIGluIGNvbW1vbj8gKEhpbnQ6IG9uZSB2YXJpYWJsZSBleHBsYWlucyB+OTAlIG9mIHRoZSBwcm9ibGVtcy4pCgpgYGB7cn0KZmxpZ2h0cyAlPiUKICBhbnRpX2pvaW4ocGxhbmVzLCBieSA9ICJ0YWlsbnVtIikgJT4lCiAgY291bnQoY2FycmllcikKP3BsYW5lcwpgYGAKCioqQSBtaXNzaW5nIGB0YWlsbnVtYCBtZWFucyB0aGF0IHRoZSBtZXRhZGF0YSBmb3IgdGhhdCBwbGFuZSBoYXNuJ3QgYmVlbiByZWNvcmRlZC4gVGhlIHZhc3QgbWFqb3JpdHkgb2YgdGhlc2UgY2FzZXMgY29tZSBmcm9tIHR3byBjYXJyaWVycyAoQUEgYW5kIE1RKS4gTG9va2luZyBhdCB0aGUgaGVscCBmaWxlIGZvciBgcGxhbmVzYCBleHBsYWlucyB0aGF0IHRoZXNlIHR3byBjYXJyaWVycyByZXBvcnQgZmxlZXQgbnVtYmVycyByYXRoZXIgdGhhbiB0YWlsIG51bWJlcnMuKioKCjIuIEZpbHRlciBmbGlnaHRzIHRvIG9ubHkgc2hvdyBmbGlnaHRzIHdpdGggcGxhbmVzIHRoYXQgaGF2ZSBmbG93biBhdCBsZWFzdCAxMDAgZmxpZ2h0cy4KCmBgYHtyfQpmbGlnaHRzXzEwMCA8LSBmbGlnaHRzICU+JQogIGNvdW50KHRhaWxudW0pICU+JQogIGZpbHRlcihuID49IDEwMCkKc2VtaV9qb2luKGZsaWdodHMsIGZsaWdodHNfMTAwKQpgYGAKCjMuIENvbWJpbmUgYGZ1ZWxlY29ub215Ojp2ZWhpY2xlc2AgYW5kIGBmdWVsZWNvbm9teTo6Y29tbW9uYCB0byBmaW5kIG9ubHkgdGhlIHJlY29yZHMgZm9yIHRoZSBtb3N0IGNvbW1vbiBtb2RlbHMuCgpgYGB7cn0Kc2VtaV9qb2luKHZlaGljbGVzLCBjb21tb24pCmBgYAoKNC4gRmluZCB0aGUgNDggaG91cnMgKG92ZXIgdGhlIGNvdXJzZSBvZiB0aGUgd2hvbGUgeWVhcikgdGhhdCBoYXZlIHRoZSB3b3JzdCBkZWxheXMuIENyb3NzLXJlZmVyZW5jZSBpdCB3aXRoIHRoZSB3ZWF0aGVyIGRhdGEuIENhbiB5b3Ugc2VlIGFueSBwYXR0ZXJucz8KCmBgYHtyfQp3b3JzdF9kZWxheXMgPC0gZmxpZ2h0cyAlPiUKICBncm91cF9ieSh5ZWFyLCBtb250aCwgZGF5LCBob3VyKSAlPiUKICBzdW1tYXJpc2UoYXZnX2RlbGF5ID0gbWVhbihkZXBfZGVsYXksIG5hLnJtID0gVFJVRSkpICU+JQogIGFycmFuZ2UoZGVzYyhhdmdfZGVsYXkpKSAlPiUKICBoZWFkKDQ4KQpzZW1pX2pvaW4od2VhdGhlciwgd29yc3RfZGVsYXlzKSAlPiUKICBnZ3Bsb3QoYWVzKHRlbXApKSArCiAgICBnZW9tX2hpc3RvZ3JhbSgpCmBgYAoKKipJdCdzIHBvc3NpYmxlIHRvIHBsb3QgaGlzdG9ncmFtcyBmb3IgdGhlIGRpZmZlcmVudCB3ZWF0aGVyIHZhcmlhYmxlcyBpbiBhIHNlYXJjaCBmb3IgcGF0dGVybnMuIFRoZSB0ZW1wZXJhdHVyZSBleGFtcGxlIGFib3ZlIHN1Z2dlc3RzIHRoYXQgc29tZXRpbWVzIHRoZSBkZWxheXMgaGFwcGVuZWQgb24gcXVpdGUgY29sZCBkYXlzIGJ1dCBtb3JlIG9mIHRoZW0gaGFwcGVuZWQgaW4gbWlsZCBvciB3YXJtIHdlYXRoZXIuKioKCjUuIFdoYXQgZG9lcyBgYW50aV9qb2luKGZsaWdodHMsIGFpcnBvcnRzLCBieSA9IGMoImRlc3QiID0gImZhYSIpKWAgdGVsbCB5b3U/IFdoYXQgZG9lcyBgYW50aV9qb2luKGFpcnBvcnRzLCBmbGlnaHRzLCBieSA9IGMoImZhYSIgPSAiZGVzdCIpKWAgdGVsbCB5b3U/CgpgYGB7cn0KYW50aV9qb2luKGZsaWdodHMsIGFpcnBvcnRzLCBieSA9IGMoImRlc3QiID0gImZhYSIpKQpgYGAKCioqVGhpcyBzaG93cyBmbGlnaHRzIHRoYXQgd2VudCB0byBhIGRlc3RpbmF0aW9uIG5vdCBsaXN0ZWQgaW4gdGhlIGFpcnBvcnRzIGRhdGFiYXNlIC0gZnJvbSBhIHF1aWNrIGluc3BlY3Rpb24gYW5kIHNlYXJjaCwgbWFueSBvZiB0aGVzZSBzZWVtIHRvIGJlIHRvIGFpcnBvcnRzIGluIFB1ZXJ0byBSaWNvIG9yIGVsc2V3aGVyZSBpbiB0aGUgQ2FyaWJiZWFuLCBpZSBvdXRzaWRlIHRoZSBtYWlubGFuZCBVbml0ZWQgU3RhdGVzLioqCgpgYGB7cn0KYW50aV9qb2luKGFpcnBvcnRzLCBmbGlnaHRzLCBieSA9IGMoImZhYSIgPSAiZGVzdCIpKQpgYGAKCioqVGhpcyBzaG93cyBhaXJwb3J0cyB0aGF0IGFyZSBsaXN0ZWQgaW4gdGhlIGFpcnBvcnRzIGRhdGFiYXNlIGJ1dCB3aGljaCB3ZXJlbid0IHRoZSBkZXN0aW5hdGlvbiBmb3IgYW55IGZsaWdodHMgaW4gdGhlIGZsaWdodHMgZGF0YWJhc2UuKioKCjYuIFlvdSBtaWdodCBleHBlY3QgdGhhdCB0aGVyZeKAmXMgYW4gaW1wbGljaXQgcmVsYXRpb25zaGlwIGJldHdlZW4gcGxhbmUgYW5kIGFpcmxpbmUsIGJlY2F1c2UgZWFjaCBwbGFuZSBpcyBmbG93biBieSBhIHNpbmdsZSBhaXJsaW5lLiBDb25maXJtIG9yIHJlamVjdCB0aGlzIGh5cG90aGVzaXMgdXNpbmcgdGhlIHRvb2xzIHlvdeKAmXZlIGxlYXJuZWQgYWJvdmUuCgpgYGB7cn0KIyBJZGVudGlmeSBhbnkgcGxhbmVzIChieSBgdGFpbG51bWAgdGhhdCBkaWRuJ3QgaGF2ZSBvbmx5IGEgc2luZ2xlIGNhcnJpZXIgcmVjb3JkZWQgZm9yIHRoZWlyIGZsaWdodCByZWNvcmRzKQptdWx0aV9jYXJyaWVyIDwtIGZsaWdodHMgJT4lCiAgY291bnQodGFpbG51bSwgY2FycmllcikgJT4lCiAgY291bnQodGFpbG51bSkgJT4lCiAgZmlsdGVyKG5uICE9IDEpCnNlbWlfam9pbihmbGlnaHRzLCBtdWx0aV9jYXJyaWVyKSAlPiUKICBjb3VudCh0YWlsbnVtLCBjYXJyaWVyKQpgYGAK